<?php
/**
 * CK 开发框架
 * User: Clake
 * Date: 15/6/15
 * Time: 22:50
 */

namespace CK\Database;


use CK\Core\Config;
use CK\Util\Arr;
use CK\Util\IO\File;

class DBA {
    //database object
    /**
     * @var \PDO
     */
    private $_mdb;
    /**
     * @var \PDO
     */
    private $_rdb;
    //current database type
    public $db_type;
    //datebase name
    private $_db_name;
    //database table result save dirtory
    private $_table_dir = "cachedata/dba";
    //database query
    /**
     * @var \PDOStatement
     */
    public $h_query;
    public $sql_str;
    //database connect var
    private $_db_var;
    private $_windows = false;
    //auto addslashes column
    private $_adds = false;
    private $_querynum = 0;
    //current error type
    private $_error_type = 0;
    //数据库是否已经打开
    private $_active = false;
    //表前缀
    private $_table_prefix = '';
    //validate rules
    private $_rules = [
        //是否加入限制长度
        'match'=>'/^(.+?)(\d+)-(\d+)$/',
        //任意字符匹配
        '*'=>'/[\w\W]+/',
        //数字
        'n'=>'/^\d+$/',
        //浮点数字
        'f'=>'/^\d+(\.?)\d{0,3}$/',
        //非特殊字符串匹配,中文,字符,空格,数字
        's'=>'/^[\x{4E00}-\x{9FA5}\x{f900}-\x{fa2d}\w\.\s\d]+$/',
        //邮编
        'p'=>'/^[0-9]{6}$/',
        //手机号
        'm'=>'/^13[0-9]{9}$|14[0-9]{9}|15[0-9]{9}$|18[0-9]{9}$/',
        //Email地址
        'e'=>"/^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$/",
        //URL地址
        'url'=>'/^(\w+:\/\/)?\w+(\.\w+)+.*$/',
        //日期
        'd'=>'/^\d{4}-\d{1,2}-\d{1,2}$/',
        //日期时间
        'dt'=>'/^\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:?\d{0,2}$/'
    ];
    //buffer
    private $buffer = false;

    //Database Type
    const DBA_MYSQL = "mysql";
    const DBA_MSSQL = "mssql";
    const DBA_SQLITE = "sqlite";

    //Database query type
    const DBA_ASSOC = "ASSOC";
    const DBA_NUM = "NUM";

    //Database error type
    /**
     * 不对错误作出任何操作
     */
    const DBA_ERR_NONE = 0;
    /**
     * 输出错误,但不停止执行
     */
    const DBA_ERR_CONTINUE = 1;
    /**
     * 抛出一个错误,并停止执行代码
     */
    const DBA_ERR_DEBUG = 2;

    /**
     * 数据库配置项
     * @var array
     */
    private $_conf = [
        //要连接的数据库类型,DBA::DBA_MYSQL|DBA::DBA_SQLITE|DBA::DBA_MSSQL
        'type'=>\CK\Database\DBA::DBA_MYSQL,
        //表前缀
        'db_prefix'=>'',
        'adds'=>true,
        //主写数据库可以为数据多个服务器
        'master' => [
            'db_host'=>'localhost',
            'db_name'=>'',
            'db_user'=>'',
            'db_pass'=>'',
            //可选
            'db_port'=>3306
        ],
        //从数据库可以为多个服务器,可选的
        'slave'=>[]
    ];

    /**
     * 实例化的数据库连接类PDO
     * @var array
     */
    private static $_instance=[];

    /**
     * 实例化数据库连接
     * @param string $name
     *
     * @return static
     */
    public static function inst($name='main') {
        if (!isset(self::$_instance[$name])) {
            self::$_instance[$name] = new self($name);
        }
        return self::$_instance[$name];
    }

    /**
     * 初始化数据操作类
     * @param string $conf_name 配置节点名称
     */
    public function __construct($conf_name='main') {
        //得到应用默认数据库配置
        $conf = Config::inst(CK_DEF_CONF)->get('database.'.$conf_name);
        if (is_array($conf)) {
            $this->_conf = Arr::mrg($this->_conf,$conf);
        }
        $this->db_type = $this->_conf['type'];
        $this->_adds = isset($this->_conf['adds']) ? $this->_conf['adds'] : true;
        $this->set_error_mode(self::DBA_ERR_DEBUG);
    }

    public function __destruct() {
        $this->close();
    }

    public function close() {
        if (method_exists($this->_mdb, "close")) {
            @$this->_mdb->close();
        }
        if (method_exists($this->_rdb, "close")) {
            @$this->_rdb->close();
        }
    }
    /**
     * 打开数据库连接
     *
     * @param string $conf 打开数据连接配置
     * mysql & mssql 配置项
     * $conf = [
     *    'db_host' => 'localhost',
     *    'db_name' => 'mysql',
     *    'db_user' => 'root',
     *    'db_pass' => '123123',
     *    'db_port' => 3306
     * ]
     * sqlite 配置项
     * $conf = [
     *     'db_name' => './sqlite.db'
     * ]
     */
    public function changeConnection($conf) {
        if (method_exists($this->_mdb, "close")) {
            @$this->_mdb->close();
        }
        $this->_mdb = null;
        $this->openConnection($conf);
    }

    /**
     * 切换数据库
     * @param string $db_name <要切换的数据库名>
     */
    public function changeDatabase($db_name) {
        $this->_db_var['db_name'] = $db_name;
        $this->changeConnection($this->_db_var['db_name']);
    }
    /**
     * 打开数据库连接
     *
     * @param string $db_conf <打开数据连接配置>
     * mysql & mssql 配置项
     * $conf = [
     *    'db_host' => 'localhost',
     *    'db_name' => 'mysql',
     *    'db_user' => 'root',
     *    'db_pass' => '123123',
     *    'db_port' => 3306
     * ]
     * sqlite 配置项
     * $conf = [
     *     'db_name' => './sqlite.db'
     * ]
     */
    public function openConnection($db_conf=null) {
        if ($this->_active) {
            return;
        }
        try {
            if (empty($db_conf)) {
                $db_conf = $this->_conf;
            }
            //数据库类型
            $type = $db_conf['type'];
            //主写数据库
            $master = is_assoc($db_conf['master'])?$db_conf['master']:array_rand($db_conf['master']);
            //从读数据库
            if (isset($db_conf['slave']) && !empty($db_conf['slave'])) {
                $slave = is_assoc($db_conf['slave'])?$db_conf['slave']:array_rand($db_conf['slave']);
            } else {
                $slave = null;
            }

            $this->_mdb = new \PDO($this->buildDsn($master,$type), $master['db_user'], $master['db_pass'], empty($db_conf['pdo_options'])?null:$db_conf['pdo_options']);
            if ($slave !== null) {
                $this->_rdb = new \PDO($this->buildDsn($slave,$type), $slave['db_user'], $slave['db_pass'], empty($db_conf['pdo_options'])?null:$db_conf['pdo_options']);
            } else {
                $this->_rdb = $this->_mdb;
            }

            $this->_db_var = $db_conf;
            $this->db_type = $type;
            $this->_db_name = $master['db_name'];
            $this->_table_prefix = $db_conf['db_prefix'];
            $this->_active = true;
        } catch(\PDOException $exception) {
            $this->halt_error('Can not connected to '.ucfirst($type),var_export($db_conf,true));
        }
    }

    private function buildDsn($conf,$type=self::DBA_MYSQL) {
        switch ($type) {
            case self::DBA_MYSQL:
                $arr = [
                    'host='.$conf['db_host'],
                    'dbname='.$conf['db_name'],
                    'port='.$conf['db_port'],
                    'charset='.isset($conf['db_charset'])?$conf['db_charset']:'utf8'
                ];
                $dsn = 'mysql:'.join(';',$arr);
                break;
            case self::DBA_MSSQL:
                if (stristr($_SERVER["OS"], "windows") === false) {
                    $arr = [
                        'host='.$conf['db_host'],
                        'dbname='.$conf['db_name']
                    ];
                    $dsn = 'dblib:'.join(';',$arr);
                } else {
                    $arr = [
                        'Server='.$conf['db_host'],
                        'Database='.$conf['db_name']
                    ];
                    $dsn = 'sqlsrv:'.join(';',$arr);
                }
                break;
            case self::DBA_SQLITE:
                $dsn = 'sqlite:'.$conf['db_name'];
                break;
        }
        return $dsn;
    }

    /**
     * 得到一个表查询类
     * @param string $table <表名>
     * @param bool $is_cache [可选] <是否启用缓存>
     * @param int $cache_expire [可选] <缓存时间以秒为单位,默认为0永久缓存>
     * @param string $cache_key [可选] <缓存自定名称,默认为SQL语句的编码>
     * @param bool $key_append [可选] <是否把缓存自定名追加到原始KEY的后面>
     * @return Table <返回表查询类实例>
     */
    public function getTable($table,$is_cache=false,$cache_expire=120,$cache_key=null,$key_append=false) {
        return new Table($this, $table, $is_cache, $cache_expire,$cache_key,$key_append);
    }

    /**
     * 得到一个存储过程的操作类实例
     * @param string $pro_name <存储过程名>
     * @return product <返回一个存储过程操作类实例>
     */
    public function getProduct($pro_name) {
        return new Product($this->_mdb, $pro_name);
    }

    /**
     * 得到一个多表删除类实例
     * @param string $table <要删除的表名>
     * @return MultiDeleteTable <返回一个多表删除类实例>
     */
    public function getMultiDelete($table) {
        return new MultiDeleteTable($this, $table);
    }

    /**
     * 开始事务
     */
    public function beginTrans() {
        $this->_mdb->beginTransaction();
    }

    /**
     * 回滚事务
     */
    public function rollBack() {
        $this->_mdb->rollBack();
    }

    /**
     * 提交事务
     */
    public function commit() {
        $this->_mdb->commit();
    }

    /**
     * 得到DBA的查询次数
     * @return number <返回一个查询次数的int数字>
     */
    public function get_querynum() {
        return $this->_querynum;
    }

    /**
     * 得到当前执行的SQL语句
     * @return string <一个SQL语句字符串>
     */
    public function get_query_string() {
        return $this->sql_str;
    }

    public function query_buffer($flag) {
        $this->buffer = $flag;
        if ($this->buffer) {
            $this->_mdb->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
        }
    }

    /**
     * 得到一个表的数据结构
     * @param string $table_name <表名称>
     * @return array <返回一个表结构的array>
     */
    public function get_table_columns($table_name) {
        if (File::Exists($this->_table_dir."/".$this->_db_name."/$table_name.dba")) {
            require_once $this->_table_dir."/".$this->_db_name."/$table_name.dba";
            return eval("\${$table_name}_tb;");
        }

        return null;
    }

    /**
     * 插入一条数据到表
     * @param array:assoc $row <要插入到表的assoc型数组,插入多条请用 array[assoc] 这样放入>
     * @param string $table <表名称>
     * @param bool $mt [可选] <是否插入多条记录,如果打开这项会先循环数组>
     * @return boolean <是否成功插入数据>
     */
    public function insert_row($row,$table,$mt=false) {
        if (is_array($row)) {
            $table = $this->format($table);
            $column = $valstr = '';
            if ($mt === true && $this->db_type == self::DBA_MYSQL) {
                $values = "";
                for ($i=0;$i<count($row);$i++) {
                    $valstr = "";
                    if ($this->_adds) {
                        $row[$i] = daddslashes($row[$i]);
                    }
                    foreach ($row[$i] as $key=>$value) {
                        $valstr .= $value === null ? "NULL," : "'$value',";
                    }
                    $valstr = substr($valstr,0,strlen($valstr)-1);
                    $values .= "($valstr),";
                    if ($i === 0) {
                        $columns = array_keys($row[$i]);
                        for ($k=0;$k<count($columns);$k++) {
                            $columns[$k] = $this->format($columns[$k]);
                        }
                        $column = join(",", $columns);
                    }
                }
                $values = substr($values,0,strlen($values)-1);
                $sql = "INSERT INTO $table ($column) VALUES $values";
            } else {
                if ($this->_adds) {
                    $row = daddslashes($row);
                }
                foreach ($row as $key => $value) {
                    $column .= $this->format($key).",";
                    $valstr .= $value === null ? "NULL," : "'$value',";
                }
                $column = substr($column,0,strlen($column)-1);
                $valstr = substr($valstr,0,strlen($valstr)-1);
                $sql = "INSERT INTO $table ($column) VALUES ($valstr)";
            }

            $flag = $this->execute_sql($sql);
            $this->sql_str = $sql;
            return $flag;
        }
        return false;
    }

    /**
     * 修改一条记录
     * @param array:assoc $row <要修改的assoc数组>
     * @param string $table <表名>
     * @param string [可选] $where_field <修改数据的条件>
     * @param string [可选] $joins <是否连接其它表>
     * @return boolean <是否修改成功>
     */
    public function update_row($row,$table,$where_field=null,$joins=null) {
        if (is_array($row)) {
            if ($this->_adds) {
                $row = daddslashes($row);
            }
            $column = $where = '';
            $tmp = [];
            foreach ($row as $key =>$value) {
                $field = $this->explain_column($key);
                if ($field['icon'] && in_array($field['icon'] ,['+','-'])) {
                    $tmp[] = $this->format($field['column']).' = '.$this->format($field['column']).' '.$field['icon'].' '.$value;
                } else {
                    $value = $value===null?"NULL":"'$value'";
                    $tmp[] = $this->format($field['column'])."=$value";
                }
            }
            $column = join(',',$tmp);
            if ($where_field && is_array($where_field)) {
                $where = $this->where_recursion($where_field,'AND',$table);
            }

//            $join_str = "";
//
//            if ($joins && is_array($joins) && count($joins) > 0) {
//                foreach ($joins as $join) {
//                    foreach ($join as $table => $column) {
//                        $join_str .= "INNER JOIN".$this->format($table).".".$this->format($column);
//                    }
//                }
//            }
            $format_table = $this->format($table);
            $sql = "UPDATE $format_table SET $column ";
            if (!empty($where)) {
                $sql .= 'WHERE '.$where;
            }
            $flag = $this->execute_sql($sql);
            $this->sql_str = $sql;
            return $flag;
        }
        return false;
    }

    /**
     * 删除数据库记录
     * @param string $table <要删除数据的表名称>
     * @param string $where_field [可选] <删除数据的条件>
     * @return boolean <是否删除成功>
     */
    public function delete_row($table,$where_field=null) {
        $format_table = $this->format($table);
        $where = "";
        if ($where_field && is_array($where_field)) {
            $where = $this->where_recursion($where_field,'AND',$table);
        }
        $sql = "DELETE FROM $format_table ";
        if (!empty($where)) {
            $sql .= 'WHERE '.$where;
        }
        $flag = $this->execute_sql($sql);
        $this->sql_str = $sql;
        return $flag;
    }

    /**
     * 清除表所有的数据
     * @param string $table <表名>
     * @return bool <返回是否成功>
     */
    public function delete_table($table) {
        $sql = "TRUNCATE TABLE ".$this->format($table);
        $this->sql_str = $sql;
        return $this->execute_sql($sql);
    }

    /**
     * 格式化字段名
     * @param $column
     *
     *
     * @return string
     */
    public function format($column) {
        if ($this->db_type == self::DBA_MYSQL) {
            return "`$column`";
        } else {
            return "[$column]";
        }
    }

    /**
     * 格式化 WHERE 字段
     * @param        $column
     * @param        $value
     * @param string $table_name
     *
     * @return string
     */
    public function format_where($column,$value,$table_name="") {
        if (is_array($value)) {
            if ($table_name == "") {
                $column = $this->format($column);
            } else {
                $column = $this->format($table_name).".".$this->format($column);
            }
            $format_str = " AND $column IN (";
            foreach ($value as $val) {
                $format_str .= "'$val',";
            }
            $format_str = substr($format_str,0,strlen($format_str)-1).")";
        } else {
            $value = trim($value);
            $exp = explode(" ",$value);
            if ($table_name == "") {
                $column = $this->format($column);
            } else {
                $column = $this->format($table_name).".".$this->format($column);
            }
            if (count($exp) == 1) {
                $format_str = " AND $column='$value'";
            } elseif (count($exp) == 2) {
                $exp[0] = strtoupper($exp[0]);
                if ($exp[0] == "OR" || $exp[0] == "AND") {
                    $format_str = " {$exp[0]} $column={$exp[1]}";
                } else {
                    $format_str = " AND $column {$exp[0]} {$exp[1]}";
                }
            } elseif (count($exp) == 3) {
                $exp[0] = strtoupper($exp[0]);
                $format_str = " {$exp[0]} $column {$exp[1]} {$exp[2]}";
            } else {
                $where = array_splice($exp,0,2);
                $rp_v = implode(" ", $exp);
                $where[0] = strtoupper($where[0]);
                $format_str = " {$where[0]} $column {$where[1]} $rp_v";
            }
        }
        return $format_str;
    }

    /**
     * 新解释类medoo column方式
     * @param array  $field ['column'=>'','icon'=>'']
     * @param        $value
     * @param string $table_name
     *
     * @return string
     */
    public function new_format_where($field,$value,$table_name="") {
        $column = $field['column'];
        $icon = '=';
        if ($field['icon']) {
            $icon = $field['icon'];
        }
        //如果在列上有表名就直接使用列表名
        if ($field['table']) {
            $table_name = $field['table'];
        }

        if ($table_name == "") {
            $column = $this->format($column);
        } else {
            $column = $this->format($table_name).".".$this->format($column);
        }

        if (is_array($value)) {
            $icon_str = 'IN';
            if ($icon === '!=') {
                $icon_str = 'NOT IN';
            }
            $format_str = " $column $icon_str (";
            foreach ($value as $val) {
                $val = daddslashes($val);
                $format_str .= "'$val',";
            }

            $format_str = substr($format_str,0,strlen($format_str)-1).")";
        } else {
            if (is_null($value)) {
                $icon_str = 'IS NULL';
                if ($icon === '!=') {
                    $icon_str = 'IS NOT NULL';
                }
                $format_str = " $column $icon_str";
            } else {
                $value = daddslashes($value);
                $format_str = " $column $icon '$value'";
            }
        }

        return $format_str;
    }

    /*** 处理条件循环
     * @param $field
     * @param $icon
     * @param $table
     *
     * @return string
     */
    public function where_recursion($field,$icon,$table) {
        $tmp = [];
        foreach ($field as $column => $value) {
            if (strtoupper($column) == 'AND' || strtoupper($column) == 'OR') {
                $tmp[] = $this->where_recursion($value,$column,$table);
            } else {
                $tmp[] = $this->new_format_where($this->explain_column($column),$value,$table);
            }
        }
        $where_str = '('.join(str_fix(' ',$icon),$tmp).')';
        return $where_str;
    }

    /**
     * 解释column是否为medoo格式
     * @param $column
     *
     * @return array
     */
    public function explain_column($column) {
        $field = [
            'table'=>null,
            'column' => null,
            'icon'   => null
        ];
        $reg = '/(.+?)\[(\+|-|!|>|<|<=|>=|like)\]/si';
        $flag = preg_match($reg,$column,$match);
        if ($flag) {
            $field['column'] = $match[1];
            $field['icon']   = $match[2] == '!' ? '!=' : $match[2];
        } else {
            $field['column'] = $column;
        }
        $column_arr = explode('.',$field['column']);
        if (count($column_arr) === 2) {
            $field['table'] = $column_arr[0];
            $field['column'] = $column_arr[1];
        }
        return $field;
    }

    /**
     * 执行一个SQL语句并返回是否成功
     * @param string $sql <要执行的SQL语句>
     *
     * @return boolean <返回是否执行成功>
     * @throws \Exception
     */
    public function execute_sql($sql) {
        if (!$this->_active) {
            $this->openConnection();
        }
        $flag = false;
        try {
            $flag = $this->_mdb->exec($sql);
            $this->_querynum++;
        } catch(\PDOException $e) {
            $this->halt_error($e->getMessage(),$sql);
        }

        if ($this->_mdb->errorCode() != '00000') {
            $this->halt_error(null,$sql);
        }
        $this->sql_str = $sql;
        return $flag === false ? false : true;
    }

    /**
     * 执行一个SQL语句并返回是否成功,execute_sql 的别名
     * @param string $sql <要执行的SQL语句>
     * @return boolean <返回是否执行成功>
     */
    public function exec($sql) {
        return $this->execute_sql($sql);
    }

    /**
     * 执行一个SQL语句,并返回是否成功,并且可以通过 fetch 方法得到结果集
     * @param string $sql <要执行的SQL语句>
     * @return boolean <返回是否成功>
     */
    public function query($sql) {
        if (!$this->_active) {
            $this->openConnection();
        }
        try {
            $this->h_query = $this->_rdb->query($sql);
            $this->_querynum++;
        } catch(\PDOException $e) {
            $this->halt_error($e->getMessage());
        }
        if ($this->_mdb->errorCode() != '00000') {
            $this->halt_error(null,$sql);
        }
        $this->sql_str = $sql;
        return $this->h_query?true:false;
    }

    /**
     * 只得到一条查询结果
     * @param string $type [可选] <要得到结果集的数组类型,默认为得到ASSOC类型>
     * @return NULL|mixed <有值返回array,无值返回NULL>
     */
    public function fetch_one($type=self::DBA_ASSOC) {
        if (!$this->h_query) {
            return null;
        }
        return $this->h_query->fetch($this->get_query_type($type));
    }

    /**
     * 得到查询的所有结果集
     * @param string $type [可选] <要得到结果集的数组类型,默认为得到ASSOC类型>
     * @return NULL|mixed <有值返回多维数组,无值返回NULL>
     */
    public function fetch_all($type=self::DBA_ASSOC) {
        if (!$this->h_query) {
            return null;
        }
        $rows = $this->h_query->fetchAll($this->get_query_type($type));
        return $rows;
    }

    /**
     * table_select 内部调用方法,得到当前表的所有行数
     * @param string $table <表名>
     * @param string $join_str <表连接字符>
     * @param string $where_str <表条件字符>
     * @param string $group_str <表分组字符>
     * @param string $select_str <表选择字符>
     * @param string $distinct_str <表唯一字符>
     * @return integer <返回表记录总条数>
     */
    public function get_num_rows($table,$join_str,$where_str,$group_str,$select_str,$distinct_str) {
        $table = $this->format($table);
        $sql = "SELECT count({$distinct_str}*) FROM $table $join_str $where_str $group_str";
        $flag = $this->query($sql);
        return $flag ? $this->h_query->fetchColumn(0) : 0;
    }

    public function get_query_type($type) {
        return $type == self::DBA_NUM ? \PDO::FETCH_NUM:\PDO::FETCH_ASSOC;
    }

    /**
     * 得到最后插入的记录ID
     * @return string <返回插入记录最后一条的ID>
     */
    public function get_insert_id() {
        return $this->_mdb->lastInsertId();
    }

    /**
     * 设置在执行操作时对所有的字段进行编码处理
     * @param bool $flag <true or false>
     */
    public function setAutoAdds($flag) {
        $this->_adds = $flag;
    }

    /**
     * 得到出错信息
     * @return string|boolean <有错返回错误字符串,无错返回false>
     */
    public function error() {
        if ($this->_mdb->errorCode() != '00000') {
            return $this->_mdb->errorInfo()[2];
        } else {
            return false;
        }
    }

    /**
     * 设置DBA的错误模式
     * @param int $type
     */
    public function set_error_mode($type) {
        $this->_error_type = $type;
    }

    /**
     * 调式出错的信息
     * @param string $msg 错误信息
     * @param string $sql 执行的SQL语句
     * @throws \Exception
     */
    public function halt_error($msg = null,$sql = null) {
        $msg = $msg?$msg:$this->_mdb->errorInfo()[2];
        $sql = $sql ? $sql : $this->sql_str;
        switch ($this->_error_type) {
            case self::DBA_ERR_NONE:
                break;
            case self::DBA_ERR_CONTINUE:
                echo "DBA Error:<br/>";
                echo "Error Msg:<br/>";
                echo $msg.'<br/>';
                echo "Error Sql:<br/>";
                echo $sql;
                break;
            case self::DBA_ERR_DEBUG:
                $error_msg = "DBA Error:\n
				Error Msg:\n
				{$msg}\n
				Error Sql:\n
				{$sql}";
                throw new \Exception($error_msg);
                break;
        }
    }

    /**
     * 验证要插入,更新的数据是否正常
     * @param array $data <要验证的数据array>
     * @param array $check <验证的正则表达式>
     * @return boolean|array <返回是否验证成功,成功返回正常可插入数据,不成功返回false>
     */
    public function check($data,$check=null) {
        $list = [];
        if (is_assoc($data)) {
            $list[] = $data;
            $type = 'accos';
        } elseif (is_array($data)) {
            $list = $data;
            $type = 'array';
        } else {
            return false;
        }

        if ($type == "accos") {
            $result = $this->check_data_one($list[0],$check);
        } elseif ($type == "array") {
            $result = [];
            foreach ($list as $one) {
                if ($rs = $this->check_data_one($one,$check)) {
                    $result[] = $rs;
                }
            }
        }

        return $result;
    }

    /**
     * 检查一条array记录是否是规则内的数据
     *
     * @param      $data
     * @param null $check
     *
     * @return array|bool
     */
    private function check_data_one($data,$check=null) {
        $result = [];
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $text = $value['text'];
                $reg = $value['reg'];
            } else {
                $text = $value;
                $reg = $check[$key];
            }
            $result[$key] = $text;
            if (!$reg) {
                continue;
            }
            $flag = $this->check_data($text, $reg);
            if (!$flag) {
                $result = false;
                break;
            }

        }
        return $result;
    }

    /**
     * 检查数据是否为定义的类型
     * @param $value
     * @param $reg
     *
     * @return bool
     */
    private function check_data($value,$reg) {
        $pattern = $this->_rules[$reg];
        if (!$pattern) {
            if (preg_match($this->_rules['match'], $reg,$matches)) {
                switch ($matches[1]) {
                    case "n":
                        $pattern = '/^\d{'.$matches[2].','.$matches[3].'}$/';
                        break;
                    case "s":
                        $pattern = '/^[\x{4E00}-\x{9FA5}\x{f900}-\x{fa2d}\w\.\s\d]{'.$matches[2].','.$matches[3].'}$/u';
                        break;
                    case "*":
                        $pattern = '/[\w\W]{'.$matches[2].','.$matches[3].'}/';
                        break;
                }
            } else {
                return false;
            }
        }

        $flag = false;
        if (preg_match($pattern, $value)) {
            $flag = true;
        }

        return $flag;
    }

    /**
     * @param bool $flag
     */
    public function enableStringify($flag=true) {
        $this->_mdb->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES,$flag);
        $this->_mdb->setAttribute(\PDO::ATTR_EMULATE_PREPARES,$flag);
        if ($this->_rdb) {
            $this->_rdb->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES,$flag);
            $this->_mdb->setAttribute(\PDO::ATTR_EMULATE_PREPARES,$flag);
        }
    }
}